package main

import (
	"fmt"
	sentinel "github.com/alibaba/sentinel-golang/api"
	"github.com/alibaba/sentinel-golang/core/base"
	"github.com/alibaba/sentinel-golang/ext/datasource"
	"github.com/alibaba/sentinel-golang/pkg/datasource/nacos"
	"github.com/alibaba/sentinel-golang/util"
	"github.com/nacos-group/nacos-sdk-go/clients"
	"github.com/nacos-group/nacos-sdk-go/common/constant"
	"math/rand"
	"sync/atomic"
	"time"
)

type Counter struct {
	pass  *int64
	block *int64
	total *int64
}

func main() {
	// 流量计数器, 为了流控打印日志更直观, 和集成 nacos 数据源无关
	counter := Counter{
		pass:  new(int64),
		block: new(int64),
		total: new(int64),
	}

	// nacos server地址
	serverConfigs := []constant.ServerConfig{
		*constant.NewServerConfig(
			"192.168.16.192",
			8848,
			constant.WithScheme("http"),
			constant.WithContextPath("/nacos"),
		),
	}

	clientConfig := *constant.NewClientConfig(
		constant.WithNamespaceId("public"),
		constant.WithTimeoutMs(5000),
		constant.WithNotLoadCacheAtStart(true),
		constant.WithLogDir("tmp/nacos/log"),
		constant.WithCacheDir("tmp/nacos/cache"),
		constant.WithLogLevel("debug"),
	)

	configClient, err := clients.CreateConfigClient(map[string]interface{}{
		"serverConfigs": serverConfigs,
		"clientConfig":  clientConfig,
	})
	if err != nil {
		panic(err)
	}

	// 注册 流控规则
	h := datasource.NewFlowRulesHandler(datasource.FlowRuleJsonArrayParser)
	// 创建 NacosDataSource 数据源
	nds, err := nacos.NewNacosDataSource(configClient, "sentinel-go", "flow", h)
	if err != nil {
		panic(err)
	}
	// 内部是实现连接 nacos 并 动态监控 nacos 配置文件的变化 更新 熔断限流配置
	err = nds.Initialize()
	if err != nil {
		panic(err)
	}

	//Starting counter
	go timerTask(&counter)

	//Simulation of the request
	ch := make(chan struct{})
	for i := 0; i < 10; i++ {
		go func() {
			for {
				atomic.AddInt64(counter.total, 1)
				// WithResourceType 函数用于设置资源类型。它接受一个参数 resourceType，表示资源的类型 Web、RPC、数据库等。根据不同的资源类型，可以设置不同的流控规则和降级规则
				// WithTrafficType 函数用于设置流量类型。它接受一个参数 trafficType 表示流量的类型 base.Inbound: 入站
				e, b := sentinel.Entry("test", sentinel.WithTrafficType(base.Inbound))
				if b != nil {
					atomic.AddInt64(counter.block, 1)
					// Blocked. We could get the block reason from the BlockError.
					time.Sleep(time.Duration(rand.Uint64()%10) * time.Millisecond)
					// 为什么这里 不执行 e.Exit() 因为 e = nil了
				} else {
					atomic.AddInt64(counter.pass, 1)
					// Passed, wrap the logic here.
					time.Sleep(time.Duration(rand.Uint64()%10) * time.Millisecond)
					// Be sure the entry is exited finally.
					e.Exit()
				}

			}
		}()
	}
	<-ch
}

// statistic print
func timerTask(counter *Counter) {
	fmt.Println("begin to statistic!!!")
	var (
		oldTotal, oldPass, oldBlock int64
	)
	for {
		time.Sleep(1 * time.Second)
		globalTotal := atomic.LoadInt64(counter.total)
		oneSecondTotal := globalTotal - oldTotal
		oldTotal = globalTotal

		globalPass := atomic.LoadInt64(counter.pass)
		oneSecondPass := globalPass - oldPass
		oldPass = globalPass

		globalBlock := atomic.LoadInt64(counter.block)
		oneSecondBlock := globalBlock - oldBlock
		oldBlock = globalBlock
		fmt.Println(util.CurrentTimeMillis()/1000, "total:", oneSecondTotal, " pass:", oneSecondPass, " block:", oneSecondBlock)
	}
}
